package cn.com.zero.common.auth.service.impl;

import cn.com.zero.api.auth.util.UserCacheKeyUtil;
import cn.com.zero.api.auth.vo.OnlineUserVO;
import cn.com.zero.base.util.RedisUtil;
import cn.com.zero.common.auth.service.IOnlineUserService;
import cn.com.zero.db.vo.PageParamsVO;
import cn.com.zero.db.vo.PageVO;
import cn.hutool.core.util.StrUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.util.Assert;
import org.springframework.util.CollectionUtils;

import javax.annotation.Resource;
import java.util.*;

/**
 * @author xiongxj
 * @version 1.0.0
 * @Description 在线用户相关操作的service实现类
 * @createTime 2023/3/29 14:51
 */
public abstract class AbstractOnlineUserService implements IOnlineUserService {
    protected Logger logger = LoggerFactory.getLogger(this.getClass());
    private RedisUtil<OnlineUserVO> redisUtil;

    @Override
    public PageVO<OnlineUserVO> getOnlineUsers(String userBelongToSide, PageParamsVO pageParams,
                                               List<String> searchUserIds, String loginDevice, String loginIp,
                                               Date loginBeginTime, Date loginEndTime) {
        PageVO<OnlineUserVO> pageResult = new PageVO<>();
        List<OnlineUserVO> onlineUserResult = new ArrayList<>();
        pageResult.setTotal(0);
        pageResult.setPages(0);
        pageResult.setList(onlineUserResult);

        // 获取分页参数
        Integer pageNum = pageParams.getPageNum();
        Integer pageSize = pageParams.getPageSize();
        if (pageSize == null || pageSize.compareTo(0) <= 0 || pageNum == null || pageNum.compareTo(0) <= 0) {
            // 分页参数不合法，直接返回空
            logger.debug("获取[{}]端在线用户数据时，分页参数不合法，返回空集", userBelongToSide);
            return pageResult;
        }

        // 从redis获取数据
        Set<String> onlineUserIdSet = new HashSet<>();
        List<OnlineUserVO> allOnlineUsers = getAllOnlineUsers(userBelongToSide, onlineUserIdSet);
        if (CollectionUtils.isEmpty(allOnlineUsers)) {
            return pageResult;
        }

        // 从数据库中获取详细信息，组装redis中的在线信息
        Map<String, OnlineUserVO> userDetailMap = getUsers(new ArrayList<>(onlineUserIdSet));
        // 对在线用户信息按照过滤条件进行过滤
        onlineUserResult = filterOnlineUserBySearch(allOnlineUsers, userDetailMap,
                searchUserIds, loginDevice, loginIp, loginBeginTime, loginEndTime);

        if (CollectionUtils.isEmpty(onlineUserResult)) {
            return pageResult;
        }

        // 对符合条件的数据进行排序
        String orderBy = pageParams.getOrderBy();
        sort(onlineUserResult, orderBy);

        // 符合条件的总数
        int totalSize = onlineUserResult.size();
        pageResult.setTotal(totalSize);
        // 分页对应的数据索引下标
        int beginIndex = (pageNum - 1) * pageSize;
        int endIndex = pageNum * pageSize - 1;
        if (endIndex > totalSize - 1) {
            endIndex = totalSize - 1;
        }

        // 按照分页下标索引获取数据
        List<OnlineUserVO> pageListResult = new ArrayList<>();
        for (int index = beginIndex; index <= endIndex; index++) {
            pageListResult.add(onlineUserResult.get(index));
        }
        pageResult.setPages(totalSize % pageSize == 0 ? totalSize / pageSize : totalSize / pageSize + 1);
        pageResult.setList(pageListResult);

        return pageResult;
    }

    @Override
    public boolean kickOffUser(String userBelongToSide, String userId, String loginDevice) {
        Assert.notNull(userBelongToSide, "用户所属端不允许为空");
        Assert.notNull(userId, "用户ID不允许为空");
        Assert.notNull(loginDevice, "用户登录设备不允许为空");
        // 清除在线用户、权限缓存和token签发时间缓存
        String onlineUserKey = UserCacheKeyUtil.getOnlineUserCacheKey(userBelongToSide,
                loginDevice, userId);
        String tokenIssueTimeKey = UserCacheKeyUtil.getUserTokenIssueTimeCacheKey(userBelongToSide,
                loginDevice, userId);
        String userPermissionKey = UserCacheKeyUtil.getMenuPermissionCacheKey(userBelongToSide,
                loginDevice, userId);
        redisUtil.delete(onlineUserKey, tokenIssueTimeKey, userPermissionKey);
        return true;
    }

    /**
     * 获取指定的用户信息
     *
     * @param userIds          用户ID集合
     * @return 用户详细信息Map集合，key为用户id，value为用户详细信息
     */
    protected abstract Map<String, OnlineUserVO> getUsers(List<String> userIds);

    /**
     * 获取所有的在线用户信息
     *
     * @param userBelongToSide 用户所属端
     * @param allUserIds       在线用户信息的用户ID集合
     * @return 所有的在线用户信息数据
     */
    private List<OnlineUserVO> getAllOnlineUsers(String userBelongToSide, Set<String> allUserIds) {
        List<OnlineUserVO> onlineUserList = new ArrayList<>();

        // 从redis缓存中获取所有的在线用户信息
        String onlineUserCacheKey = UserCacheKeyUtil.getOnlineUserCacheKey(userBelongToSide, null, null);
        Set<String> onlineUserCacheKeys = redisUtil.keys(onlineUserCacheKey);
        if (CollectionUtils.isEmpty(onlineUserCacheKeys)) {
            return onlineUserList;
        }
        for (String userCacheKey : onlineUserCacheKeys) {
            OnlineUserVO onlineUser = redisUtil.getObject(userCacheKey);
            if (null == onlineUser) {
                continue;
            }
            onlineUserList.add(onlineUser);
            allUserIds.add(onlineUser.getUserId());
        }
        return onlineUserList;
    }

    /**
     * 按照搜索条件对在线用户进行过滤处理，同时拼装用户的详细信息
     *
     * @param onlineUserList 原始的在线用户列表
     * @param userDetailMap  用户详细信息
     * @param searchUserIds  待过滤的用户ID列表
     * @param loginDevice    待过滤的用户登录设备
     * @param loginIp        待过滤的用户登录ip
     * @param loginBeginTime 待过滤的用户登录开始时间
     * @param loginEndTime   待过滤的用户登录结束时间
     * @return 已经过滤处理后的在线用户信息
     */
    private List<OnlineUserVO> filterOnlineUserBySearch(List<OnlineUserVO> onlineUserList, Map<String, OnlineUserVO> userDetailMap, List<String> searchUserIds,
                                                        String loginDevice, String loginIp, Date loginBeginTime, Date loginEndTime) {
        List<OnlineUserVO> afterFilteredList = new ArrayList<>();

        for (OnlineUserVO onlineUserVO : onlineUserList) {
            OnlineUserVO onlineUserDB = userDetailMap.get(onlineUserVO.getUserId());
            if (null == onlineUserDB) {
                continue;
            }
            String userId = onlineUserVO.getUserId();
            OnlineUserVO onlineUser = onlineUserDB.clone(onlineUserVO);
            // 按照搜索条件 用户ID 进行过滤
            if (!CollectionUtils.isEmpty(searchUserIds)) {
                if (searchUserIds.contains(userId)) {
                    afterFilteredList.add(onlineUser);
                }
            } else {
                afterFilteredList.add(onlineUser);
            }
            // 按照搜索条件 登录设备 进行过滤
            if (!StrUtil.isEmpty(loginDevice)) {
                if (!onlineUser.getLoginDevice().equals(loginDevice)) {
                    afterFilteredList.remove(onlineUser);
                }
            }
            // 按照搜索条件 登录IP 进行过滤
            if (!StrUtil.isEmpty(loginIp)) {
                if (!onlineUser.getLoginIp().contains(loginIp)) {
                    afterFilteredList.remove(onlineUser);
                }
            }
            // 按照搜索条件 登录开始时间和登录结束时间进行过滤
            if (loginBeginTime != null && loginEndTime != null) {
                if (onlineUser.getLoginTime().compareTo(loginBeginTime) < 0
                        || onlineUser.getLoginTime().compareTo(loginEndTime) > 0) {
                    afterFilteredList.remove(onlineUser);
                }
            }
        }

        return afterFilteredList;
    }

    /**
     * 对在线用户进行排序
     * 目前只支持登录时间倒序或者正序，若登录时间相同，则按照用户升序，若用户ID相同，则按照登录设备升序
     *
     * @param sortList 待排序的在线用户列表
     * @param orderBy  排序条件
     */
    private void sort(List<OnlineUserVO> sortList, String orderBy) {
        if (orderBy != null && orderBy.trim().equalsIgnoreCase("loginTime desc")) {
            // 按照登录时间倒序
            sortList.sort((onlineUser1, onlineUser2) -> {
                if (onlineUser1.getLoginTime().compareTo(onlineUser2.getLoginTime()) == 0) {
                    if (onlineUser1.getUserId().compareTo(onlineUser2.getUserId()) == 0) {
                        return onlineUser1.getLoginDevice().compareTo(onlineUser2.getLoginDevice());
                    }
                    return onlineUser1.getUserId().compareTo(onlineUser2.getUserId());
                }
                return onlineUser1.getLoginTime().before(onlineUser2.getLoginTime()) ? 1 : -1;
            });

        } else {
            // 按照登录时间正序
            sortList.sort((onlineUser1, onlineUser2) -> {
                if (onlineUser1.getLoginTime().compareTo(onlineUser2.getLoginTime()) == 0) {
                    if (onlineUser1.getUserId().compareTo(onlineUser2.getUserId()) == 0) {
                        return onlineUser1.getLoginDevice().compareTo(onlineUser2.getLoginDevice());
                    }
                    return onlineUser1.getUserId().compareTo(onlineUser2.getUserId());
                }
                return onlineUser1.getLoginTime().before(onlineUser2.getLoginTime()) ? -1 : 1;
            });
        }
    }

    @Resource
    public void setRedisUtil(RedisUtil<OnlineUserVO> redisUtil) {
        this.redisUtil = redisUtil;
    }
}
